package org.jflame.captcha.creator;

import org.jflame.captcha.Captcha;
import org.jflame.captcha.CaptchaUtils;
import org.jflame.captcha.RandomImgManager;
import org.jflame.captcha.validator.RectsVerifiable;
import org.jflame.captcha.validator.SingleVerifiable;
import org.jflame.commons.file.ImageHelper;
import org.jflame.commons.json.JsonHelper;
import org.jflame.commons.reflect.TypeRef;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.RandomUtils;

/**
 * 文字点选验证码生成器
 * 
 * @author charles.zhang
 */
public class WordClickCaptchaCreator implements CaptchaCreator<RectsVerifiable<Rect>,WordClickImage> {

    private final TextCaptchaCreator wordGenerator;// 随机文字生成器
    protected RandomImgManager imgManager;
    protected Font defaultFont = null;// 字体
    protected int width = 200;// 图片默认宽度
    protected int height = 120;// 图片默认高度
    protected int mislead = 0;// 混淆字数

    /**
     * 构造函数,默认随机生成4个汉字供选择
     * 
     * @param _imgManager
     */
    public WordClickCaptchaCreator(RandomImgManager _imgManager) {
        this(_imgManager, null);
    }

    public WordClickCaptchaCreator(RandomImgManager _imgManager, TextCaptchaCreator wordCreator) {
        if (wordCreator == null) {
            wordGenerator = new TextCaptchaCreator();
            wordGenerator.justChinese();
        } else {
            wordGenerator = wordCreator;
        }
        imgManager = _imgManager;
    }

    @Override
    public Captcha<RectsVerifiable<Rect>,WordClickImage> create() {
        Captcha<SingleVerifiable<String>,String> textCaptcha = wordGenerator.create();
        String drawStr = textCaptcha.getDisplay();

        // 获取背景图
        BufferedImage bgImg = null;
        try {
            bgImg = getRandomBackgroundImg();
        } catch (IOException e) {
            throw new RuntimeException("获取验证码背景图异常", e);
        }
        // 绘制生成的字符
        char[] drawChars = drawStr.toCharArray();
        Font font;
        if (defaultFont == null) {
            int size = RandomUtils.nextInt(height / 5, height / 3);
            font = new Font(Font.SANS_SERIF, Font.BOLD, size);
        } else {
            font = defaultFont;
        }
        int charWidth = font.getSize() + 2,charHeight = font.getSize() + 4;
        // ArrayList<WordRect> charLocaions = new ArrayList<>(drawChars.length);
        int avgWidth = width / drawChars.length;
        int x = 0,y,j;
        BufferedImage charImg;
        Graphics2D bgG2d = bgImg.createGraphics();
        Map<Rect,Character> charLocaions = new HashMap<>(drawChars.length);
        for (j = 0; j < drawChars.length; j++) {
            charImg = genCharImg(charWidth, charHeight, font, drawChars[j]);
            // System.out.print("j=" + j + " w=" + charImg.getWidth());
            y = RandomUtils.nextInt(10, height - charImg.getHeight());
            x = RandomUtils.nextInt(avgWidth * j, avgWidth * (j + 1) - Math.round(charImg.getWidth() * 0.8f));
            // System.out.println("x=" + x + " y=" + y);
            if (j < drawChars.length - mislead) {
                // charLocaions.add(new WordRect(x, y, charWidth, charHeight, drawChars[j]));
                charLocaions.put(new Rect(x, y, charWidth, charHeight), drawChars[j]);
            } else {
                // 最后几个字可能超出图片宽
                if (x + charWidth > width) {
                    x = avgWidth * j;
                }
            }
            // 绘制字符到背景图里
            CaptchaUtils.overlayImage(bgImg, charImg, x, y);
            // 绘制
            bgG2d.drawImage(charImg, x, y, charImg.getWidth(), charImg.getHeight(), null);
            // 重复绘制一个,加粗
            if (RandomUtils.nextBoolean()) {
                bgG2d.drawImage(charImg, x + 1, y + 1, charImg.getWidth(), charImg.getHeight(), null);
            }
        }
        bgG2d.dispose();
        // 打乱顺序,作为正确验证码.
        ArrayList<Rect> rects = new ArrayList<>(charLocaions.keySet());
        // System.out.println(JsonHelper.toJson(rects));
        Collections.shuffle(rects);
        // System.out.println(JsonHelper.toJson(rects));
        char[] correctChars = new char[rects.size()];
        for (j = 0; j < correctChars.length; j++) {
            correctChars[j] = charLocaions.get(rects.get(j));
        }
        String correctCode = new String(correctChars);
        // 绘制正确的提示文字
        BufferedImage tipImg = drawTips(correctCode);

        WordClickImage display = new WordClickImage(bgImg, tipImg, (short) correctChars.length);
        return new Captcha<>(new RectsVerifiable<Rect>(rects), display);
    }

    @Override
    public RectsVerifiable<Rect> parse(String verifiableString) {
        return JsonHelper.parseObject(verifiableString, new TypeRef<RectsVerifiable<Rect>>() {
        });
    }

    /**
     * 随机获取一张指定大小的背景图
     * 
     * @param width
     * @param height
     * @return
     * @throws IOException
     */
    private BufferedImage getRandomBackgroundImg() throws IOException {
        File bgImg = imgManager.nextFile();
        BufferedImage imageBuf = ImageIO.read(bgImg);
        if (imageBuf.getWidth() == width && imageBuf.getHeight() == height) {
            return imageBuf;
        }
        return ImageHelper.zoomedFixedSize(imageBuf, width, height);
    }

    /**
     * 绘制正确的字序提示
     * 
     * @param code
     * @param captchaParam
     * @return
     */
    private BufferedImage drawTips(String code) {
        int size = height / 5;// 字体大小取背景图高度/5
        if (size < 12) {
            size = 12;
        }
        if (size > 24) {
            size = 24;
        }
        Font font = new Font(Font.SANS_SERIF, Font.PLAIN, size);
        int imgWidth = (size + 2) * code.length();
        int imgHeight = size + 2;

        BufferedImage imgBuf = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = imgBuf.createGraphics();
        g.setFont(font);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, imgWidth, imgHeight);
        // 加几条干扰线
        Color textColor = CaptchaUtils.getRandColor();
        int i,start_x,end_x,start_y,end_y,rndNum = RandomUtils.nextInt(3, 8);
        for (i = 0; i < rndNum; i++) {
            g.setColor(textColor.darker());
            start_x = RandomUtils.nextInt(0, imgWidth);
            start_y = RandomUtils.nextInt(0, imgHeight);
            end_x = RandomUtils.nextInt(imgWidth - 10, imgWidth);
            end_y = RandomUtils.nextInt(imgHeight - 5, imgHeight);
            g.drawLine(start_x, start_y, end_x, end_y);
        }
        g.setColor(textColor);
        g.drawString(code, 1, size);
        g.dispose();
        return imgBuf;
    }

    private BufferedImage genCharImg(int imgWidth, int imgHeight, Font font, char c) {
        BufferedImage fillRect = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = fillRect.createGraphics();

        // g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setColor(new Color(255, 255, 255, 0));
        g.fillRect(0, 0, imgWidth, imgHeight);

        g.setColor(CaptchaUtils.getRandColor(50, 250));
        g.setFont(font);
        String word = String.valueOf(c);
        Point p = ImageHelper.calcCenterPoint(g, imgWidth, imgHeight, word);
        g.drawString(word, p.x, p.y);
        g.dispose();
        BufferedImage bi = ImageHelper.rotate(fillRect, CaptchaUtils.nextInt(60));
        return bi;
    }

    public void setDefaultFont(Font defaultFont) {
        this.defaultFont = defaultFont;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    /**
     * 加入混淆字
     * 
     * @param mislead
     */
    public void enableMislead() {
        this.mislead = 1;
        wordGenerator.setCount(wordGenerator.getCount() + mislead);
    }

    public final TextCaptchaCreator getWordGenerator() {
        return wordGenerator;
    }

}
